package com.itcoon.transform.starter.proxy;

import com.itcoon.common.utils.SpringUtils;
import com.itcoon.transform.base.constants.TransformConstants;
import com.itcoon.transform.base.definition.ModelDefinition;
import com.itcoon.transform.base.definition.ModelPropertyDefinition;
import com.itcoon.transform.base.exception.TransformException;
import com.itcoon.transform.base.pool.AssembleBeanHolder;
import com.itcoon.transform.base.proxy.IProxy;
import com.itcoon.transform.base.utils.TransformServiceUtils;
import com.itcoon.transform.inject.InjectHandler;
import com.itcoon.transform.assemble.assembler.inter.MethodAssemblerComposite;
import com.itcoon.transform.assemble.handler.AssembleHandlerComposite;
import com.itcoon.transform.base.definition.InjectDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author Miaoxia Yu
 * @date 2020-10-28
 */
public class Transform4jCglibProxy<T, R> implements MethodInterceptor, IProxy<T, R> {

    private static final Logger log = LoggerFactory.getLogger(Transform4jCglibProxy.class);
    private static final AssembleHandlerComposite ASSEMBLE_HANDLER_COMPOSITE = SpringUtils.getBean(AssembleHandlerComposite.class);
    private static final MethodAssemblerComposite ASSEMBLER_COMPOSITE = SpringUtils.getBean(MethodAssemblerComposite.class);
    private static final InjectHandler INJECT_HANDLER = SpringUtils.getBean(InjectHandler.class);
    private final Class<R> targetClass;
    private final T source;
    private final List<InjectDefinition.InjectInfo> injectInfos;

    public Transform4jCglibProxy(Class<R> targetClass, T source, List<InjectDefinition.InjectInfo> injectInfos) {
        this.targetClass = targetClass;
        this.source = source;
        this.injectInfos = injectInfos;
    }

    @Override
    public <RR extends R> RR instance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);
        enhancer.setSuperclass(targetClass);
        enhancer.setInterfaces(new Class[]{IProxy.class});
        RR targetProxy =  (RR) enhancer.create();
        try {
            invokeProxySetter(targetProxy);
        } catch (InvocationTargetException | IllegalAccessException e) {
            log.error(e.getMessage(), e);
            throw new TransformException("proxy setters invoke Exception");
        }
        return targetProxy;
    }


    private <RR extends R> void invokeProxySetter(RR targetProxy) throws InvocationTargetException, IllegalAccessException{
        ModelDefinition md = AssembleBeanHolder.getInstance().getModelDefinition(targetClass, source);
        if(md != null){
            List<ModelPropertyDefinition> mpds = md.getPropertyDefinitions();
            for (ModelPropertyDefinition mpd : mpds) {
                Method targetSetter = mpd.getTargetSetter();
                if(targetSetter != null){
                    Method originGetter = mpd.getOriginGetter();
                    if(originGetter == null){
                        continue;
                    }

                    Object belongObj = TransformServiceUtils.getBelongFromValue(source, mpd.getOriginAttribute());
                    if(belongObj != null){
                        Object originGetterValue = originGetter.invoke(belongObj);
                        //编辑前先过滤injectInfos
                        List<InjectDefinition.InjectInfo> methodInjectInfos = new ArrayList<>();
                        for (InjectDefinition.InjectInfo injectInfo : injectInfos) {
                            String fullAttribute = injectInfo.getAttribute();
                            String methodAttribute = TransformServiceUtils.formatDataValue(targetSetter.getName());
                            if(fullAttribute.contains(".")){
                                String firstData = fullAttribute.substring(0, fullAttribute.indexOf("."));
                                if(firstData.equals(methodAttribute)){
                                    String attribute = fullAttribute.substring(fullAttribute.indexOf(".") + 1);
                                    InjectDefinition.InjectInfo info = new InjectDefinition.InjectInfo(attribute, injectInfo.getInjectMode(), injectInfo.getAttributeSource());
                                    methodInjectInfos.add(info);
                                }
                            }
                        }
                        Object targetSetterParameterValue = ASSEMBLER_COMPOSITE.assemble(source, targetClass, this, targetSetter,methodInjectInfos, originGetterValue);
                        targetSetter.invoke(targetProxy, targetSetterParameterValue);
                    }

                }
            }
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object result = methodProxy.invokeSuper(o, args);
        if (!method.getName().startsWith(TransformConstants.GETTER_PREFIX) || Object.class.equals(method.getDeclaringClass())){
            return result;
        }
        // 进行注入
        InjectDefinition.InjectInfo injectInfo = INJECT_HANDLER.support(method, injectInfos);
        return injectInfo == null ? result : INJECT_HANDLER.handle(targetClass, injectInfo, result);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Transform4jCglibProxy<?, ?> that = (Transform4jCglibProxy<?, ?>) o;
        return targetClass.equals(that.targetClass) &&
                source.equals(that.source) &&
                injectInfos.equals(that.injectInfos);
    }

    @Override
    public int hashCode() {
        return Objects.hash(targetClass, source, injectInfos);
    }
}
